#include "stdafx.h"
#include <process.h>

#include "hwinthread.h"

namespace harlinn
{
    namespace windows
    {
        namespace threading
        {
            // -----------------------------------------------------------------
            // ThreadHandle
            // -----------------------------------------------------------------
            HWIN_EXPORT HANDLE ThreadHandle::CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,
                                ThreadHandle* pThreadHandle,DWORD dwCreationFlags)
            {
                unsigned threadId;
                HANDLE result = (HANDLE)_beginthreadex(lpThreadAttributes,UINT(dwStackSize),ThreadStart,pThreadHandle,dwCreationFlags | CREATE_SUSPENDED,&threadId);
                if(result == nullptr)
                {
                    ThrowLastOSError();
                }
                pThreadHandle->threadId = threadId;
                return result;
            }

            HWIN_EXPORT unsigned __stdcall ThreadHandle::ThreadStart(void* lpThreadParameter)
            {
                ThreadHandle* pThreadHandle = reinterpret_cast<ThreadHandle*>(lpThreadParameter);
                DWORD result = 0;
                try
                {
                    std::shared_ptr<ThreadHandle> threadHandle = pThreadHandle->As<ThreadHandle>();
                    CurrentThreadHandle::currentThreadData->threadHandle = threadHandle;
                    result = pThreadHandle->DoOnRun();
                }
                catch(std::exception& exc)
                {

                }
                catch(...)
                {

                }
                _endthreadex(result);
                return result;
            }

            HWIN_EXPORT ThreadHandle::ThreadHandle(DWORD stackSize, bool stackSizeIsReservation)
            {
                HANDLE h = CreateThread(nullptr,stackSize,this,(stackSizeIsReservation?STACK_SIZE_PARAM_IS_A_RESERVATION:0));
                SetValue(h,true);
            }


            HWIN_EXPORT std::shared_ptr<CurrentThreadHandle> ThreadHandle::Current()
            {
                auto result = std::make_shared<CurrentThreadHandle>();
                return result;
            }

            HWIN_EXPORT ThreadHandle& ThreadHandle::Start()
            {
                if(ResumeThread(GetHANDLE()) == ((DWORD)-1))
                {
                    ThrowLastOSError();
                }
                return *this;
            }

            HWIN_EXPORT DWORD ThreadHandle::ThreadId() const
            {
                return threadId;
            }

            HWIN_EXPORT ThreadHandle::ThreadPriority_t ThreadHandle::ThreadPriority() const
            {
                int result = GetThreadPriority(GetHANDLE());
                if(result == THREAD_PRIORITY_ERROR_RETURN)
                {
                    ThrowLastOSError();
                }
                return ThreadPriority_t(result);
            }

            HWIN_EXPORT ThreadHandle& ThreadHandle::SetThreadPriority(ThreadPriority_t theThreadPriority)
            {
                if(::SetThreadPriority(GetHANDLE(),int(theThreadPriority)) == 0)
                {
                    ThrowLastOSError();
                }
                return *this;
            }

            HWIN_EXPORT DWORD ThreadHandle::Suspend()
            {
                DWORD result = SuspendThread(GetHANDLE());
                if(result == (DWORD) -1)
                {
                    ThrowLastOSError();
                }
                return result;
            }
            HWIN_EXPORT DWORD ThreadHandle::Resume()
            {
                    
                DWORD result = ResumeThread(GetHANDLE());
                if(result == (DWORD) -1)
                {
                    ThrowLastOSError();
                }
                return result;
            }

            HWIN_EXPORT DWORD ThreadHandle::ExitCode() const
            {
                DWORD result = 0;
                if(GetExitCodeThread(GetHANDLE(),&result) == 0)
                {
                    ThrowLastOSError();
                }
                return result;
            }



            HWIN_EXPORT DWORD ThreadHandle::DoOnRun()
            {
                return 0;
            }


            // -----------------------------------------------------------------
            // CurrentThreadData
            // -----------------------------------------------------------------
            HWIN_EXPORT CurrentThreadData::CurrentThreadData()
            {
                threadId = GetCurrentThreadId();
            }
            HWIN_EXPORT CurrentThreadData::~CurrentThreadData()
            {
            }


            // -----------------------------------------------------------------
            // CurrentThreadHandle
            // -----------------------------------------------------------------
            __declspec(thread) CurrentThreadData* CurrentThreadHandle::currentThreadData = nullptr;

            HWIN_EXPORT void CurrentThreadHandle::InitializeThreadData()
            {
                if(currentThreadData == nullptr)
                {
                    currentThreadData = new CurrentThreadData();
                }
            }
            HWIN_EXPORT void CurrentThreadHandle::FinalizeThreadData()
            {
                if(currentThreadData != nullptr)
                {
                    delete currentThreadData;
                    currentThreadData = nullptr;
                }
            }

            HWIN_EXPORT CurrentThreadHandle::CurrentThreadHandle()
                : Base(GetCurrentThread(),false)
            {}


            HWIN_EXPORT CurrentThreadHandle::ThreadPriority_t CurrentThreadHandle::ThreadPriority() const
            {
                int result = GetThreadPriority(GetHANDLE());
                if(result == THREAD_PRIORITY_ERROR_RETURN)
                {
                    ThrowLastOSError();
                }
                return ThreadPriority_t(result);
            }
            HWIN_EXPORT CurrentThreadHandle& CurrentThreadHandle::SetThreadPriority(ThreadPriority_t theThreadPriority)
            {
                if(::SetThreadPriority(GetHANDLE(),int(theThreadPriority)) == 0)
                {
                    ThrowLastOSError();
                }
                return *this;
            }

            HWIN_EXPORT DWORD CurrentThreadHandle::Suspend()
            {
                DWORD result = SuspendThread(GetHANDLE());
                if(result == (DWORD) -1)
                {
                    ThrowLastOSError();
                }
                return result;
            }

            HWIN_EXPORT CurrentThreadHandle& CurrentThreadHandle::Sleep(DWORD milliSeconds)
            {
                ::Sleep(milliSeconds);
                return *this;
            }

            HWIN_EXPORT bool CurrentThreadHandle::Sleep(DWORD milliSeconds,bool alertable)
            {
                if(::SleepEx(milliSeconds,alertable?TRUE:FALSE) == WAIT_IO_COMPLETION)
                {
                    return true;
                }
                return false;
            }



        };
    };
};